package com.cake.liulanxiangzhu.service.impl;

import com.alibaba.fastjson.JSON;
import com.cake.liulanxiangzhu.pojo.dto.AdminUserDTO;
import com.cake.liulanxiangzhu.security.AdminDetails;
import com.cake.liulanxiangzhu.security.LoginPrincipal;
import com.cake.liulanxiangzhu.security.TextMessageServiceImpl;
import com.cake.liulanxiangzhu.security.WeiXinDetailsServiceImpl;
import com.cake.liulanxiangzhu.service.AdminUserService;
import com.cake.liulanxiangzhu.utils.JwtUtil;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.concurrent.TimeUnit;

@Service
@Slf4j
public class AdminUserServiceImpl implements AdminUserService {

    public static final String LOGINUSER = "LoginUser:system:";
    public static final String LOGINSMS = "LoginUser:textMessage:";

    @Autowired
    @Qualifier("usernamePasswordAuthenticationManager")
    private AuthenticationManager usernamePasswordAuthenticationManager;

    @Autowired
    private TextMessageServiceImpl textMessageService;

    @Autowired
    private WeiXinDetailsServiceImpl weiXinDetailsService;

    @Autowired
    RedisTemplate redisTemplate;

    @Override
    public String loginSystem(AdminUserDTO adminUserDTO) {
        Authentication authentication = new UsernamePasswordAuthenticationToken(
                adminUserDTO.getUsername(), adminUserDTO.getPassword());//准备登录验证的数据
        log.info("开始执行{}", authentication);
        Authentication authenticate =
                usernamePasswordAuthenticationManager
                        .authenticate(authentication);//通过UserDetailsServiceImpl中的loadUserByUsername进行验证
        log.info("成功通过认证，返回{}", authenticate);
        Object principal = authenticate.getPrincipal();
        AdminDetails adminDetails = (AdminDetails) principal;//这里会获得存入loadUserByUsername时的ID和用户名等信息
        log.info("准备生成JWT");
        UUID uuid = UUID.randomUUID();//准备存入缓存的随机数，用来完成单端登录
        Map<String, Object> claims = new HashMap<>();//准备Map来存入生成token的数据
        claims.put("id", adminDetails.getId());
        claims.put("username", adminDetails.getUsername());
        claims.put("uuid",uuid.toString());
        claims.put("authorities", JSON.toJSONString(adminDetails.getAuthorities()));
        redisTemplate.opsForValue().set(LOGINUSER + adminDetails.getId() ,
                JSON.toJSONString(uuid), 30, TimeUnit.MINUTES);//存入缓存，用来完成单端登录验证
        String secretKey = "liulanxiangzhushidacake";//用于生成token的key
        Date expirationDate = new Date(System.currentTimeMillis() + 10 * 60 * 1000);//设置token的过期时间
        String jwtToken = Jwts.builder().setHeaderParam("alg", "HS256").setHeaderParam("typ", "JWT")//生成TOKEN使用的算法，固定写法
                .setClaims(claims)//用于生成token的数据
                .setExpiration(expirationDate).signWith(SignatureAlgorithm.HS256, secretKey)
                .compact();
        return jwtToken;
    }

    @Override
    public String loginBySms(AdminUserDTO adminUserDTO) {
        Authentication authentication = new UsernamePasswordAuthenticationToken(
                adminUserDTO.getPhone(), adminUserDTO.getCode());
        log.info("开始执行 {}", authentication);
        Authentication authenticate =
                textMessageService.authenticate(authentication);//通过TextMessageServiceImpl中的authenticate进行验证
        Object principal = authenticate.getPrincipal();//principal是authenticate存入的adminDetails
        AdminDetails adminDetails = (AdminDetails) principal;
        Integer id = adminDetails.getId();
        String username = adminDetails.getUsername();
        Collection<GrantedAuthority> authorities = adminDetails.getAuthorities();

        log.info("准备生成JWT");
        UUID uuid = UUID.randomUUID();
        redisTemplate.opsForValue().set(LOGINUSER + adminDetails.getId() , JSON.toJSONString(uuid), 30, TimeUnit.MINUTES);
        String token = JwtUtil.generateToken(id, username, uuid.toString(), authorities);//生成JWT的方法和用户名的方式一样
        return token;
    }

    @Override
    public String randomNum(Long phone) {//生成4位随机手机验证码
        Random random = new Random();
        StringBuilder code = new StringBuilder();
        for (int i = 0; i < 4; i++) {
            int num = random.nextInt(10);
            code.append(num);
        }
        redisTemplate.opsForValue().set(LOGINSMS+phone,code.toString(),30,TimeUnit.SECONDS);

        return code.toString();
    }

    @Override
    public String loginByWeiXin(String code) {
        Authentication authentication = new UsernamePasswordAuthenticationToken(code,null);//微信登录只传code就可以验证登录
        Authentication authenticate = weiXinDetailsService.authenticate(authentication);
        Object principal = authenticate.getPrincipal();
        AdminDetails adminDetails = (AdminDetails) principal;
        Integer id = adminDetails.getId();
        String openId = adminDetails.getUsername();
        Collection<GrantedAuthority> authorities = adminDetails.getAuthorities();
        log.info("准备生成JWT");
        UUID uuid = UUID.randomUUID();
        redisTemplate.opsForValue().set(LOGINUSER + adminDetails.getId() , JSON.toJSONString(uuid), 30, TimeUnit.MINUTES);
        String token = JwtUtil.generateToken(id, openId, uuid.toString(), authorities);
        return token;
    }

    @Override
    public void logoutSystem() {//登出的方法
        UsernamePasswordAuthenticationToken authenticationToken =
                (UsernamePasswordAuthenticationToken) SecurityContextHolder.getContext().getAuthentication();
        LoginPrincipal principal = (LoginPrincipal) authenticationToken.getPrincipal();
        Integer id = principal.getId();
        redisTemplate.delete(LOGINUSER + id);//删除缓存后，再次登录会被JWT过滤器拦截
    }
}
